﻿using System;
using System.Collections;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.XPath;
using System.IO;
using System.Text;
using InfoJetSoft.Service.Entity;
using InfoJetSoft.Service.Util;
using InfoJetSoft.Service.EscapeRule;

namespace InfoJetSoft.Service.Worker
{
	
	//负责合并XML.
	class FormBuildXMLDocByMergerWorker
	{
		public static void  buildXMLDocByMerger(Form form) {
			Hashtable paramTable = form.ParamTable;

			//从paramTable中提取出所有的XML.
			ArrayList xmlList = new ArrayList();
			for( int i = 0; true ; i ++ ){
				string mergeXml = (string)paramTable[ JetConstant.XDOC_PARAM_MERGE_XML_ + i ];
				if( mergeXml != null && mergeXml.Length > 0 ){
					xmlList.Add( mergeXml );
				}else{
					break;
				}
			}
			if( xmlList.Count <= 0 ){
				throw new InfoJetException("No merge doc.", "FBXDBMW01");
			}
			
			//以第一个文档作为主文档.
			string firstXml = (string)xmlList[ 0 ];
			//先构造文档,但是先不进行标号(MergeFinished)之后的内容,合并之后再操作.
			FormBuildXMLDocByXMLWorker.buildXMLDoc( form, firstXml );

			//从XML列表中删除第一个,剩下的用于合并到主文档中.
			xmlList.RemoveAt( 0 );

			if( form.Manifest.ImportParameters.UseScriptHandler )
			{
				//使用脚本进行合并.
				FormCodeDispatchWorker.OnMergeRequest(form,xmlList);
			}
			else
			{
				//使用内建功能进行合并.
				mergeXmlList(form, xmlList);
			}
		
			//(MergeFinished)完成合并
			//根据InfoJetService.config中的ContextValue定义,更新Xml.
			//FormBuildXMLDocByMergerWorker可能被buildFormByMerger或buildFormByTemplateMerger调用.
			string command = (string) form.ParamTable[JetConstant.XDOC_PARAM_COMMAND];
			if( command.Equals( JetConstant.CMD_BUILD_BY_TEMPLATEMERGER ) )
			{
				//如果是buildFormByTemplateMerger,调用UpdateWhileOpenTemplate.
				FormConfigContextValue.UpdateWhileOpenTemplate( form );
			}
			else if( command.Equals( JetConstant.CMD_BUILD_BY_MERGER ) )
			{
				//如果是buildFormByMerger,调用UpdateWhileOpenXml.
				FormConfigContextValue.UpdateWhileOpenXml( form );
			}

			//完成合并,修改PI,发送文档构造完成事件.
			FormUtil.formatPiHref( form );

			//CID 0451 //2009.11.09 //Begin
			SchemaPSVI.clearXsiNil( form );
			//CID 0451 //2009.11.09 //End

			//CID 0159 //2008.12.03 //Begin
			form.JetContext.OnXmlDocumentReadyEvent( form );
			//CID 0159 //2008.12.03 //End
			form.JetContext.OnXmlDocumentCompletedEvent( form );
			ScriptControlContext.updateContext(form);
		}

		//将XML列表中的XML合并到主文档中.
		private static void mergeXmlList(Form form, ArrayList xmlList)
		{
			foreach( string mergeXml in xmlList )
			{

				//直接构造用于合并的XmlDocument,不需要进行文档升级.
                //CID 1022 //2011.10.16 //Begin
                XmlDocument mergeDoc = null;
                if (form.Manifest.FormFieldsCacheEnabled)
                {
                    mergeDoc = DOMUtil.buildDocPreserveWhitespace(System.Text.Encoding.UTF8.GetBytes(mergeXml));
                }
                else
                {
                    mergeDoc = DOMUtil.buildDoc(System.Text.Encoding.UTF8.GetBytes(mergeXml));
                }
                //CID 1022 //2011.10.16 //End
                mergeXmlDoc(form, mergeDoc);
			}
		}

		//将给定文档合并到主文档中.
		private static void mergeXmlDoc(Form form, XmlDocument mergeDoc)
		{
			NamespaceResolver mergeNsResolver = new NamespaceResolver( mergeDoc );
			//合并文档主要是根据XmlToEdit的定义进行合并.这是XmlToEdit的另一个主要用途.
			ArrayList xmlToEditList = form.Manifest.XmlToEditList;
			xmlToEditList = removeDuplicateXmlToEdit( xmlToEditList );
			//CID 0243 //2009.03.12 //Begin
			if( form.Manifest.ImportParameters != null && form.Manifest.ImportParameters.ImportSource != null && form.Manifest.ImportParameters.ImportSource.ActionList != null )
			{
				//CID 0243 //2009.03.14 //Begin
				ImportActionOrder actionOrder = new ImportActionOrder( form.Manifest.ImportParameters.ImportSource.ActionList );
				ImportAction importAction = actionOrder.getNext();
				int index = 1;
				//CID 0243 //2009.03.14 //End
				while( importAction != null )
				{
					removeXmlToEditByImportAction( xmlToEditList, importAction );

					XmlNodeList mainItemNodeList = DOMUtil.selectNodes( form.XmlDoc, importAction.source.fullXPath, 0, form.XmlNsResolver );
					XmlNodeList mergeItemNodeList = DOMUtil.selectNodes( mergeDoc, importAction.source.fullXPath, 0, mergeNsResolver );
					if( importAction.skip || importAction.keepAttribute )
					{
						//Do nothing.
					}
					else
					{
						if( importAction.insert != null )
						{
							ImportActionRepeatingContext importActionRepeatingContext = new ImportActionRepeatingContext( form, importAction, mainItemNodeList, mergeItemNodeList, mergeNsResolver );
							importActionRepeatingContext.doMerge();
						}
						if( importAction.concatenate != null )
						{
							ImportActionTextFieldContext importActionTextFieldContext = new ImportActionTextFieldContext( form, importAction, mainItemNodeList, mergeItemNodeList, mergeNsResolver );
							importActionTextFieldContext.doMerge();
						}
					}
					removeMergeItemNodeList( mergeItemNodeList );

					//CID 0243 //2009.03.14 //Begin
					importAction = actionOrder.getNext();
					index ++;
					//CID 0243 //2009.03.14 //End
				}
			}
			//CID 0243 //2009.03.12 //End
			//CID 0243 //2009.03.14 //Begin
			XmlToEditOrder editOrder = new XmlToEditOrder( xmlToEditList );
			int editIndex = 1;
			XmlToEdit xmlToEdit = editOrder.getNext();
			//CID 0243 //2009.03.14 //End
			while( xmlToEdit != null )
			{
				XmlNodeList mainItemNodeList = DOMUtil.selectNodes( form.XmlDoc, xmlToEdit.Item, 0, form.XmlNsResolver );
				XmlNodeList mergeItemNodeList = DOMUtil.selectNodes( mergeDoc, xmlToEdit.Item, 0, mergeNsResolver );
				//根据不同类型的XmlToEdit进行合并.
				if( xmlToEdit.Component.Equals( JetConstant.CMP_xCollection )  )
				{
					//CID 0237 //2009.03.02 //Begin
					ImportActionRepeatingContext importActionRepeatingContext = new ImportActionRepeatingContext( form, xmlToEdit, mainItemNodeList, mergeItemNodeList, mergeNsResolver );
					//CID 0237 //2009.03.02 //End
					//CID 0238 //2009.03.08 //Begin
					importActionRepeatingContext.doMerge();
					//CID 0238 //2009.03.08 //End
				}
				else if( xmlToEdit.Component.Equals( JetConstant.CMP_xTextList ) )
				{
					//CID 0237 //2009.03.02 //Begin
					ImportActionRepeatingContext importActionRepeatingContext = new ImportActionRepeatingContext( form, xmlToEdit, mainItemNodeList, mergeItemNodeList, mergeNsResolver );
					//CID 0237 //2009.03.02 //End
					//CID 0238 //2009.03.08 //Begin
					importActionRepeatingContext.doMerge();
					//CID 0238 //2009.03.08 //End
				}
				else if( xmlToEdit.Component.Equals( JetConstant.CMP_xOptional ) )
				{
					mergexOptional( form, mainItemNodeList, mergeItemNodeList );
				}
				else if( xmlToEdit.Component.Equals( JetConstant.CMP_xReplace ) )
				{
					mergexOptional( form, mainItemNodeList, mergeItemNodeList );
				}
				else if( xmlToEdit.Component.Equals( JetConstant.CMP_xField ) )
				{
					//CID 0270 //2009.04.08 //type == "rich".
					if( xmlToEdit.Type != null && xmlToEdit.Type.Equals( "rich" ) )
					{
						//CID 0238 //2009.03.09 //Begin
						ImportActionTextFieldContext importActionTextFieldContext = new ImportActionTextFieldContext( form, xmlToEdit, mainItemNodeList, mergeItemNodeList, mergeNsResolver );
						importActionTextFieldContext.doMerge();
						//CID 0238 //2009.03.09 //End
					}
				}
                //CID 0181 //2009.01.11 //Begin
				removeMergeItemNodeList( mergeItemNodeList );
				//CID 0181 //2009.01.11 //End

				//CID 0243 //2009.03.14 //Begin
				xmlToEdit = editOrder.getNext();
				editIndex ++;
				//CID 0243 //2009.03.14 //End
			}
			//CID 0243 //2009.03.14 //Begin
			XmlNodeList attrNodeList = DOMUtil.selectNodes( mergeDoc, "//@*", 0, mergeNsResolver );
			mergeSingleAttrNodes( form, attrNodeList );
			//CID 0243 //2009.03.14 //End
			//CID 0181 //2009.01.11 //Begin
			//查找所有的text(),这时所有group已经都被删除,所以找到的text,应该就是单值的field,而不是group中的field.
			XmlNodeList mergeTextNodeList = DOMUtil.selectNodes( mergeDoc, "//text()", 0, mergeNsResolver );
			mergeSingleTextNodes( form, mergeTextNodeList );
			//CID 0181 //2009.01.11 //End
		}

		//CID 0243 //2009.03.14 //Begin
		private static void mergeSingleAttrNodes( Form form, XmlNodeList mergeAttrNodeList )
		{
			foreach( XmlAttribute attr in mergeAttrNodeList )
			{
				if( !( 
					( attr.NamespaceURI != null && ( attr.NamespaceURI.Equals( JetConstant.URI_XMLNS ) || attr.NamespaceURI.Equals( JetConstant.URI_XSI ) ) )
					|| attr.Name.Equals( "xml:lang" ) 
					) )
				{
					string path = DOMUtil.getElementPath( attr.OwnerElement );
					XmlElement element = DOMUtil.selectANode( form.XmlDoc, path, form.XmlNsResolver ) as XmlElement;
					if( element != null )
					{
						element.SetAttribute( attr.LocalName, attr.NamespaceURI, attr.Value );
					}
				}
			}
		}
		//CID 0243 //2009.03.14 //End

		//CID 0181 //2009.01.11 //Begin
		private static void mergeSingleTextNodes( Form form, XmlNodeList mergeTextNodeList )
		{
			ArrayList mergeElements = new ArrayList();
			foreach( XmlText mergeTextNode in mergeTextNodeList )
			{
				if( mergeTextNode.ParentNode is XmlElement )
				{
					if( !mergeElements.Contains( mergeTextNode.ParentNode ) )
					{
						mergeElements.Add( mergeTextNode.ParentNode );
					}
				}
			}
			foreach( XmlElement mergeElement in mergeElements )
			{
				string path = DOMUtil.getElementPath( mergeElement );
				XmlElement element = DOMUtil.selectANode( form.XmlDoc, path, form.XmlNsResolver ) as XmlElement;
				if( element == null )
				{
					string containerPath = path.Substring( 0, path.LastIndexOf( "/" ) );
					XmlElement containerElement = DOMUtil.selectANode( form.XmlDoc, containerPath, form.XmlNsResolver ) as XmlElement;
					if( containerElement != null )
					{
						XmlElement cloneMergeItemElement = CloneUtil.cloneElement( form.XmlDoc, mergeElement );
						bool tryAppend  = true;
						foreach( XmlNode childNode in containerElement.ChildNodes )
						{
							if( childNode is XmlElement )
							{
								if( SchemaUtil.canInsertBefore( form, containerElement, cloneMergeItemElement, childNode as XmlElement ) )
								{
									containerElement.InsertBefore( cloneMergeItemElement, childNode);
									tryAppend = false;
									break;
								}
							}
						}
						if( tryAppend )
						{
							if( SchemaUtil.canAppendChild( form, containerElement, cloneMergeItemElement ) )
							{
								containerElement.AppendChild( cloneMergeItemElement );
							}
						}
					}
				}
			}
		}
		//CID 0181 //2009.01.11 //End

		//CID 0181 //2009.01.11 //Begin
		//删除处理过的mergeItemNodeList,以便更好的查找field.
		private static void removeMergeItemNodeList( XmlNodeList mergeItemNodeList )
		{
			foreach( XmlNode mergeItemNode in mergeItemNodeList )
			{
				if( mergeItemNode is XmlElement )
				{
					XmlElement mergeItemElement = mergeItemNode as XmlElement;
					if( mergeItemElement.ParentNode != null )
					{
						mergeItemElement.ParentNode.RemoveChild( mergeItemElement );
					}
				}
				else if( mergeItemNode is XmlAttribute )
				{
					XmlAttribute mergeItemAttribute = mergeItemNode as XmlAttribute;
					if( mergeItemAttribute.OwnerElement != null )
					{
						mergeItemAttribute.OwnerElement.Attributes.Remove( mergeItemAttribute );
					}
				}
			}
		}
		//CID 0181 //2009.01.11 //End

		//删除Item重叠的XmlToEdit.
		private static ArrayList removeDuplicateXmlToEdit( ArrayList xmlToEditList ){
			ArrayList newList = new ArrayList();
			foreach( XmlToEdit xmlToEdit in  xmlToEditList ){
				bool shouldAdd = true;
				for( int i = 0; i < newList.Count; i ++ ){
					XmlToEdit xte = (XmlToEdit) newList[ i ];
					if( xmlToEdit.Item.StartsWith( xte.Item ) ){
						shouldAdd = false;
						break;
					}
					if( xte.Item.StartsWith( xmlToEdit.Item ) ){
						newList.Remove( xte );
						i --;
					}
				}
				if( shouldAdd ){
					newList.Add( xmlToEdit );
				}
			}
			return newList;
		}

		//CID 0243 //2009.03.12 //Begin
		//删除已经定义在ActionTable的XmlToEdit,不需要再Merge.
		private static void removeXmlToEditByImportAction( ArrayList xmlToEditList, ImportAction importAction )
		{
			ArrayList deleteList = new ArrayList();
			foreach( XmlToEdit xmlToEdit in xmlToEditList )
			{
				if( xmlToEdit.Item.Equals( importAction.source.fullXPath ) )
				{
					deleteList.Add( xmlToEdit );
				}
			}
			foreach( XmlToEdit deleteXmlToEdit in deleteList )
			{
				xmlToEditList.Remove( deleteXmlToEdit );
			}
		}
		//CID 0243 //2009.03.12 //End

		private static void mergexOptional( Form form, XmlNodeList mainItemNodeList, XmlNodeList mergeItemNodeList ){
			if( mainItemNodeList.Count <= 0 && mergeItemNodeList.Count > 0 ){
				XmlNode firstMergeNode = mergeItemNodeList.Item( 0 );
				if( firstMergeNode is XmlElement ){
					XmlElement firstMergeElement = (XmlElement) firstMergeNode;
					XmlElement parentMergeElement = (XmlElement) firstMergeNode.ParentNode;
					string parentPath = DOMUtil.getElementPath( parentMergeElement );
					XmlElement mainParentElement = (XmlElement)DOMUtil.selectANode( form.XmlDoc, parentPath, form.XmlNsResolver );
					if( mainParentElement != null ){
						XmlElement cloneFirstMergeElement = CloneUtil.cloneElement( form.XmlDoc, firstMergeElement );
						//CID 0180 //2009.03.03 //Begin
						bool tryAppend = true;
						//CID 0180 //2009.03.03 //End
						if( SchemaUtil.getChildElementCount( mainParentElement ) > 0 )
						{
							foreach( XmlNode childNode in mainParentElement.ChildNodes ){
								if( childNode is XmlElement ){
									XmlElement childElement = (XmlElement) childNode;
									if( SchemaUtil.canInsertBefore( form, mainParentElement, cloneFirstMergeElement, childElement ) ){
										mainParentElement.InsertBefore( cloneFirstMergeElement, childElement );
										//CID 0180 //2009.03.03 //Begin
										tryAppend = false;
										//CID 0180 //2009.03.03  //End
										break;
									}
								}
							}
						}
						//CID 0180 //2009.03.03  //Begin
						if( tryAppend ){
						//CID 0180 //2009.03.03 //End
							if( SchemaUtil.canAppendChild( form, mainParentElement, cloneFirstMergeElement ) ){
								mainParentElement.AppendChild( cloneFirstMergeElement );
							}
						}
					}
				}
			}
		}
	}
}